﻿using System;
using System.Collections.Generic;
using System.Linq;
using CoreEx.Common.Extensions;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;

namespace CoreEx.Common.Implementation.Extensions
{                      
    /// <summary>
    /// A class that is capable of converting a noun between its singular and plural form. 
    /// </summary>
    [Implements(typeof(IPluralizer))]    
    public class Pluralizer : IPluralizer, IInitialize
    {
        
        /// <summary>
        /// Gets the <see cref="IPluralRules"/> list instance that contains 
        /// all the pluralization rules.
        /// </summary>
        protected IPluralRules PluralRules { get; private set; }


        /// <summary>
        /// Converts the <paramref name="singularNoun"/> into its plural form.
        /// </summary>
        /// <param name="singularNoun">The target singular noun</param>
        /// <returns>
        /// The plural form of the <paramref name="singularNoun"/>.
        /// </returns>
        public string ToPlural(string singularNoun)
        {            
            return Convert(singularNoun, PluralRules.AsSingularDictionary());
        }

        /// <summary>
        /// Converts the <paramref name="pluralNoun"/> into its singular form.
        /// </summary>
        /// <param name="pluralNoun">The target plural noun</param>
        /// <returns>
        /// The singular form of the <paramref name="pluralNoun"/>.
        /// </returns>
        public string ToSingular(string pluralNoun)
        {
            return Convert(pluralNoun, PluralRules.AsPluralDictionary());
        }

        /// <summary>
        /// Converts the <paramref name="targetNoun"/> into plural or singular form 
        /// based on the list of <paramref name="rules"/>.
        /// </summary>
        /// <param name="targetNoun">The target noun to convert.</param>
        /// <param name="rules">A list of conversion rules indexed by its plural or singular key.</param>
        /// <returns>The converted noun.</returns>
        protected string Convert(string targetNoun, IDictionary<string, string> rules)
        {
            var orderedRules = rules.OrderByDescending(kvp => kvp.Key.Length);
            string oldSuffix = string.Empty;
            string newSuffix = string.Empty;
            
            foreach (var kvp in orderedRules)
            {
                if (!targetNoun.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase)) 
                    continue;
                oldSuffix = kvp.Key;
                newSuffix = kvp.Value;
                break;
            }

            var targetNounWithoutSuffix = targetNoun.Substring(0, targetNoun.Length - oldSuffix.Length);
            var casingTemplate = targetNoun.Substring(targetNoun.Length - oldSuffix.Length);

            if (string.IsNullOrEmpty(casingTemplate))
                casingTemplate = targetNoun.Last().ToString();

            return targetNounWithoutSuffix + AdjustSuffixCasing(casingTemplate, newSuffix);
            
        }

        /// <summary>
        /// Adjust the casing of the new suffix.
        /// </summary>
        /// <param name="template">Template used to infer the suffix casing.</param>
        /// <param name="newSuffix">The suffix that will have its casing adjusted.</param>
        /// <returns>The <paramref name="newSuffix"/> in correct casing.</returns>
        private static string AdjustSuffixCasing(string template, string newSuffix)
        {
            if (template.ToUpper() == template)
                return newSuffix.ToUpper();
            if (template.ToLower() == template)
                return newSuffix.ToLower();

            if (Char.IsUpper(template.First()))
                return newSuffix.Substring(0, 1).ToUpper() + newSuffix.Substring(1);
            
            return newSuffix;
        }

        /// <summary>
        /// Initializes this service instance.
        /// </summary>
        /// <param name="source">The <see cref="IServiceContainer"/> that created this instance.</param>
        public void Initialize(IServiceContainer source)
        {
            PluralRules = source.GetService<IPluralRules>();
        }
    }
}
